from model.Player import Player
from model.GameContext import GameContext, GameState, init_game_context
from model.Building import Building
from model.BuildingType import BuildingType
from model.Vertex import Vertex
from model.Road import Road
from datastore.GameContextDataStore import GameContextDataStore
from constants.CatanGameConstants import CatanGameConstants
import logging
import random
import copy

logging.basicConfig(level=logging.DEBUG)
counter = 0

def generate_player_id():
#  return random.randint(0, 100) 
  global counter
  counter += 1
  return counter

def host_game(token):
  logging.debug("hosting game")
  # create default GameContext
  game_context = init_game_context(token)
  GameContextDataStore.put(game_context)
  return __broadcast_state(game_context.id, token)\
    + __broadcast_data(GameContextDataStore.get_game_headers(), ClientMethod.update_gamelist)
  # TODO: send update_game_list

def send_message(token, game_id, msg, to_token_id = None):
  logging.debug("sendMessage from: %s, game: %d, msg: %s" %(token, game_id, msg))
  to = {"token" : to_token_id} if to_token_id else {"game_id": game_id}  
  return __broadcast_data({"token":token, "msg":msg}, 
                          ClientMethod.message_received, 
                          **to)
  # TODO: send update_game_list
  
def register():
  token = generate_player_id()
  logging.debug("registering user %d" % token)
  data = GameContextDataStore.get_game_headers()
  data['token'] = token
  return __broadcast_data(data, 
                          ClientMethod.registration_accepted, 
                          token = token)

def get_game_list():
  logging.debug("getting game list")
  return GameContextDataStore.get_all()

def join_game(token, game_id):
  logging.debug("User %s joining game %d" % (token, game_id))
  game_context = GameContextDataStore.get(game_id)
  err_msg = ""
  if game_context == None:
    err_msg = "game with id " + str(game_id) + " does not exist"
  elif len(game_context.players) >= CatanGameConstants.MAX_PLAYERS:
    err_msg = "game is full"
  if err_msg: 
    return __broadcast_data({"msg":err_msg}, 
                          ClientMethod.error, 
                          token = token)
  game_context.players.append(Player(token))
  return __broadcast_state(game_id = game_context.id) +\
   __broadcast_data(GameContextDataStore.get_game_headers(), ClientMethod.update_gamelist)
  
def start_game(token, game_id):
  logging.debug("starting game %d" % game_id)
  game_context = __get_game_context(game_id)
  if game_context:
    game_context.distr_resources(True)
    game_context.game_state = GameState.INIT1 
  pass
  return __broadcast_state(game_id = game_id) +\
          __broadcast_data(GameContextDataStore.get_init_board(),
                            ClientMethod.init_board, 
                            game_id = game_id)

def init_settlement(token, game_id, vertex_id):
  logging.debug("placing initial settlement")
  if __building_exists(game_id, vertex_id):
    return __broadcast_data({"msg":"a building already exists there"}, 
                      ClientMethod.error, 
                      token = token)
  else:
    __place_building(token, game_id, vertex_id, BuildingType.Settlement)
    return __broadcast_state(game_id = game_id)
  
def init_road(token, game_id, edge_id):
  logging.debug("placing initial road with game_id %d" % game_id)
  if (__road_exists(game_id, edge_id)):
    __place_road(token, game_id, edge_id)
    return __broadcast_data({"msg":"a road already exists there"}, 
                      ClientMethod.error, 
                      token = token)
  else:
    return __broadcast_state(game_id = game_id)
  
def buy_settlement(token, game_id, vertex_id):
  logging.debug("buying settlement")
  if __building_exists(game_id, vertex_id):
    return __broadcast_data({"msg":"a building already exists there"}, 
                      ClientMethod.error, 
                      token = token)
  else:
    __place_building(token, game_id, vertex_id, BuildingType.Settlement, __update_resources, CatanGameConstants.SETTLEMENT_COST)
    return __broadcast_state(game_id = game_id)

def update_thief(token, game_id, hex_id):
  context = __get_game_context(game_id)
  context.set_thief_hex_id(hex_id)
  return __broadcast_state(game_id = game_id)
  
def buy_road(token, game_id, edge_id):
  logging.debug("buying road")
  __place_road(token, game_id, edge_id, __update_resources, CatanGameConstants.ROAD_COST)
  return __broadcast_state(game_id = game_id)
  
def upgrade_settlement(token, game_id, vertex_id):
  logging.debug("upgrading settlement")
  __remove_building_if_exists(token, game_id, vertex_id)
  __place_building(token, game_id, vertex_id, BuildingType.City, __update_resources, CatanGameConstants.CITY_COST)
  return __broadcast_state(game_id = game_id)

def end_turn(token, game_id, dice = None):
  context = __get_game_context(game_id)
  msg = []
  if context.update_points():
    #game's over!
    msg += __broadcast_state(game_id = game_id)
    GameContextDataStore.delete_game(game_id)
    
  else:
    context.next_player()
    if context.game_state == GameState.PLAYING_GAME:
      if dice:
        context.set_dice(dice)
      else:
        context.roll_dice()
    context.distr_resources()
    GameContextDataStore.put(context)
    msg += __broadcast_state(game_id = game_id)
  return msg
  
def end_games_with_player(token):
  logging.info("end games with player token %s" % token)
  games = GameContextDataStore.get_games_with_player(token)
  msgs = []
  for game in games:
    game.winner_player = -1
    game.game_state = GameState.GAME_ENDED
    msgs += __broadcast_state(game_id = game.id)
    GameContextDataStore.delete_game(game.id)
  return msgs

def __building_exists(game_id, vertex_id):
  game_context = GameContextDataStore.get(game_id)
  for player in game_context.players:
    for building in player.buildings:
      if building.vertex == vertex_id:
        return True;
  return False

def __road_exists(game_id, edge_id):
  game_context = GameContextDataStore.get(game_id)
  for player in game_context.players:
    for road in player.roads:
      if road.edge == edge_id:
        return True;
  return False
  
def __update_resources(costs, player):
  for cost in costs:
    player.update_resources(cost)
    
def __remove_building_if_exists(token, game_id, vertex_id):
  game_context = GameContextDataStore.get(game_id)
  [player, index] = GameContextDataStore.get_player_in_game(token, game_context)
  if player != None:
    for building in player.buildings:
      if building.vertex == vertex_id:
        player.buildings.remove(building)
    GameContextDataStore.put(game_context)
  else:
    logging.debug("player with token %s is not found" % token)

def __place_building(token, game_id, vertex_id, building_type, update_resources_func=None, cost=None):
  game_context = GameContextDataStore.get(game_id)
  [player, index] = GameContextDataStore.get_player_in_game(token, game_context)
  if player != None:
    if update_resources_func != None:
      update_resources_func(cost, player)
    building = Building(index, vertex_id, building_type)
    player.buildings.append(building)
    player.update_points()
    GameContextDataStore.put(game_context)
  else:
    logging.debug("player with token %s is not found" % token)

def __place_road(token, game_id, edge_id, update_resources_func=None, cost=None):
  game_context = GameContextDataStore.get(game_id)
  [player, index] = GameContextDataStore.get_player_in_game(token, game_context)
  if player != None:
    if update_resources_func != None:
      update_resources_func(cost, player)
    road = Road(edge_id, index)
    player.roads.append(road)
    GameContextDataStore.put(game_context)

def __broadcast_state(game_id=None, token=None):
  return __broadcast_data(__get_game_context(game_id), 
                          ClientMethod.update_context,
                          game_id, 
                          token)

def __broadcast_data(data, method, game_id=None, token=None):
  msg = {}
  if token:
    msg[token] = __create_data(data, method)
    return [msg]
  if game_id:
    context = __get_game_context(game_id)
    players = context.players
  else:
    players = GameContextDataStore.get_all_players()
  for player in players:
    msg[player.token] = __create_data(data, method)
  return [msg]

def __create_data(data, method):
  return {'data':data,'type':method.type}

def __get_game_context(game_id):
  game_context = GameContextDataStore.get(game_id)
  if game_context == None:
    logging.debug("game with id " + str(game_id) + " does not exist")
  return game_context

class ClientMethod:
  def __init__(self, type):
    self.type = type

ClientMethod.registration_accepted = ClientMethod("registration_accepted")
ClientMethod.update_gamelist = ClientMethod("update_gamelist")
ClientMethod.update_context = ClientMethod("update_context")
ClientMethod.init_board = ClientMethod("init_board")
ClientMethod.error = ClientMethod("error")
ClientMethod.message_received = ClientMethod("message_received")
